package cn.felord.app.security;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
import org.springframework.util.Assert;

import java.io.IOException;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

/**
 *
 * JwtToken 生成
 *
 * @author Dax
 * @since 0 :30  2018/9/21
 */
@Slf4j
public class JwtTokenGenerator {
    private KeyPair keyPair;


    /**
     * Instantiates a new Jwt token generator.
     *
     * @param keyPairFactory           the key pair factory
     * @param jwtCertificateProperties the jwt certificate properties
     */
    public JwtTokenGenerator(KeyPairFactory keyPairFactory, JwtCertificateProperties jwtCertificateProperties) {
        this.keyPair = keyPairFactory.create(jwtCertificateProperties.getKeyPath(), jwtCertificateProperties.getKeyAlias(), jwtCertificateProperties.getKeyPass());
    }

    /**
     * 生成jwt 字符串.
     *
     * @param jwtClaims the jwt claims
     * @return the string
     * @throws JsonProcessingException the json processing exception
     */
    public String jwtToken(JwtClaims jwtClaims) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        String jwtClaimsStr = mapper.writeValueAsString(jwtClaims);
        //"hive.jks", "hive", "key_hive"
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        RsaSigner signer = new RsaSigner(privateKey);

        return JwtHelper.encode(jwtClaimsStr, signer).getEncoded();
    }

    /**
     * 解码校验jwt 并获取 载体信息.
     *
     * @param jwtToken the jwt token
     * @return the jwt claims
     */
    public JwtClaims decodeAndVerify(String jwtToken) {
        try {
            Assert.hasText(jwtToken, "jwt token must not be bank");
            RSAPublicKey rsaPublicKey = (RSAPublicKey) this.keyPair.getPublic();
            SignatureVerifier rsaVerifier = new RsaVerifier(rsaPublicKey);
            Jwt jwt = JwtHelper.decodeAndVerify(jwtToken, rsaVerifier);
            return getJwtClaims(jwt);

        } catch (Exception e) {
            log.error("jwtToken : {}  fail to decodeAndVerify : {}", jwtToken, e.getMessage());
        }
        return null;
    }

    private JwtClaims getJwtClaims(Jwt jwt) {
        String claims = jwt.getClaims();
        ObjectMapper objectMapper = new ObjectMapper();
        JwtClaims jwtClaims = null;
        try {
            jwtClaims = objectMapper.readValue(claims, JwtClaims.class);
        } catch (IOException e) {
            log.error("json fail to convert jwtClaims instance: {}", e.getMessage());
        }
        return jwtClaims;
    }

}
